package zhaolongli_Entity;

import java.util.ArrayList;
import java.util.Hashtable;


/** hold all the data in the Decision Lines Event*/

public class Model {
	int M;		// number of round
	int N = 1;		// number of lines
	int edgeAdded;	// number of edges added
	int choiceAdded;	// number of choice added, max number equal N
	Choice choices[ ];	// All the choices
	Choice result[ ];    // result of the deision line
	Hashtable<Integer, Edge> edges[];	// list of edges
	String question;		// question of decision Line
	int gapWidth;		// width between two vertical lines
	int firstLineOffset;	// distant between first line and the boundary
	boolean ready = false;
	
	public Model(int n, int m){
		this.N = n;
		this.M = m;
		this.edgeAdded = 0;
		this.choiceAdded = 0;
		setN(n);
		
		// Need to Initialized gapWidth based on N.
		// Need to initialized offset based on N.
		if(N == 3){
			this.gapWidth = 165;
			this.firstLineOffset = 130;
		}
		else if (N == 4){
			this.gapWidth = 125;
			this.firstLineOffset = 110;
		}
		else if (N == 5){
			this.gapWidth = 108;
			this.firstLineOffset = 80;
		}
		else if (N == 6){
			this.gapWidth = 91;
			this.firstLineOffset = 70;
		}
		else if (N == 7){
			this.gapWidth = 81;
			this.firstLineOffset = 54;
		}
		else if (N == 8){
			this.gapWidth = 72;
			this.firstLineOffset = 44;
		}
		
	}

	
	/** Add an edge to the model.
	 * @param edge 	new edge to be added 
	 * @return
	 */	
	public boolean addEdge (Edge edge){
		
		/** One extra click on the Line view to compute the priority of the choice */
		
		if(this.edgeAdded >= (this.M * this.N)){

			if (result == null){
				System.out.println("Maximum number of edge reach, The priority of the choices is: ");
				result = new Choice[this.N];			
				result = this.computerPriority();

				this.printChoice(result);
			}else{
				System.out.println("Result:");
				this.printChoice(result);
			}
			return false;
		}
		
		/* check to see if the current column or adjacent columns 
		 * has edge that has the same height, if there exist such edge,
		 * then the new edge if invalid.
		 */
		
		if (edge.getColumn() > 0 && edge.getColumn() < (N-2)){
			if (checkOpen(edge.getColumn(), edge.getHeight()) 
					&& checkOpen((edge.getColumn()-1), edge.getHeight()) 
					&& checkOpen((edge.getColumn()+1), edge.getHeight()) ){
				edges[edge.getColumn()].put(edge.getHeight(), edge);	
				this.edgeAdded ++;
				return true;
			}
			else{
				System.out.println("Edge invalid");
				return false;
			}			
		}
		// when the edge is placed at the leftmost column
		else if(edge.getColumn() == 0){
			if (checkOpen(edge.getColumn(), edge.getHeight())
					&& checkOpen((edge.getColumn()+1), edge.getHeight()) ){
				edges[edge.column].put(edge.height, edge);	
				this.edgeAdded ++;
				return true;
			}
			else{
				System.out.println("Edge invalid");
				return false;
			}
		}
		//when the edge is placed at the rightmost column
		
		else if(edge.getColumn() == (N-2)){
			if (checkOpen(edge.getColumn(), edge.getHeight()) 
					&& checkOpen((edge.getColumn()-1), edge.getHeight()) ){
				edges[edge.column].put(edge.height, edge);	
				this.edgeAdded ++;
				return true;
			}
			else{
				System.out.println("Edge invalid");
				return false;
			}
		}
	return false;	
	}
	
	
	/** check to see if the spot is open for an edge
	 * @param int column	column to be checked
	 * @param int height	height to check at
	 * @return boolean		true if spot is open, false if spot is taken
	 */
	
	boolean checkOpen(int column,int height){
		int i;
		for(i = (height-7); i<(height+7); i++){
			if(edges[column].get(i) != null){
				return false;
			}
		}
		return true;
	}
	
	
	
	
	/** 
	 * add a choice into the event.
	 * @param Choice choice		choice to be added
	 */
	public void addChoice(Choice choice){
		/* check to see if the paticular spot already exist a choice */
		if(this.choices[choice.index] == null){
			this.choices[choice.index] = choice;
			for (int i = 0; i < getN(); i++){
				if(choices[i] == null)
					return;
			}
			this.ready = true;
		}
		else System.out.println("Choice already exist");
	}
	
	
/**
 * set value M, the number of rounds.
 * @param m		number of rounds to set to.
 */
	public void setM(int m){
		this.M = m;
	}
	
	/**
	 * get the value of M, the number of Rounds.
	 * @return
	 */
	public int getM(){
		return this.M;
	}
	
	/**
	 * set the number of Participants.
	 * @param n		number of participants.
	 */
	
	private void setN(int n){
		this.N = n;
		this.choices = new Choice[N];
		this.edges = new Hashtable[N-1];
		for(int i = 0; i < edges.length; i++){
			edges[i] = new Hashtable<Integer, Edge>();
		}
	}
	
	/** Get the number of participants*/
	public int getN(){
		return this.N;
	}
	
	/** set the questions of the model with the Given String */
	
	public void setQuestion(String question){
		this.question = question;
	}
	/** get the Question of he model */
	
	public String getQuestion(){
		return this.question;
	}
	
	/** get the gap width */
	public int getGap(){
		return this.gapWidth;
	}
	
	/** get the width between first line and the boundary */
	public int getOffset(){
		return this.firstLineOffset;
	}
	
	/** check content of the decision lines for debugging reason */
	
	public void checkContent(){
		System.out.println(this.toString());
		System.out.println("M = "+this.M+" N = "+this.N);
		System.out.println("Question = "+this.question);
		System.out.println("choices "+ this.choices.length);
		System.out.println("edges "+this.edges.length);
	}
	
	/** check the content of choices for test reason*/
/*	
	public void checkChoices(){
		int i;
		for(i = 0; i < this.choices.length; i++){
			try{
				System.out.println(choices[i].toString());
			}catch(NullPointerException ex){
				System.out.println("this one is NULL");
			}			
		}
	}
	*/
	
	/** 
	 * compute the priority of the choice.
	 * @return String
	 */
	
	public Choice[] computerPriority(){
		// make a copy of the Choices so that the content of the original copy will not change
		
		Choice tempList[] = new Choice[this.getN()];
		System.arraycopy(choices, 0, tempList, 0, getN());
		
		Choice newList[] = new Choice[this.N];
		for(int i = 0; i< this.N; i++){
			runThrough(tempList[i]);
			newList[tempList[i].index] = tempList[i];
		}
		return newList;
	}
	
	
	/**
	 * Compute Priority of a given choice
	 * @param choice	choice which we need to compute priority
	 */
	void runThrough(Choice choice) {
		int i;
		for(i = 60; i <= 340; i++){
			if(choice.getIndex() != 0 && choice.getIndex() != this.getN() - 1){
				if (edges[choice.getIndex() - 1].get(i) != null){
					choice.changeIndex(choice.getIndex() - 1);
				}
				else if(edges[choice.getIndex()].get(i) != null){
					choice.changeIndex(choice.getIndex() + 1);
				}
			}
			else if(choice.getIndex() == 0){
				if(edges[choice.getIndex()].get(i) != null){
					choice.changeIndex(choice.getIndex() + 1);
				}				
			}
			else if(choice.getIndex() == this.getN() - 1){
				if(edges[choice.getIndex() - 1].get(i) != null){
					choice.changeIndex(choice.getIndex() - 1);
				}				
			}
		}				
	}
	
/** get all the edges
 * 
 * @return
 */
	public Hashtable[] getEdges(){
		return edges;
	}
		/** indicate if Dicision lines is ready to accept edge */
	
	public boolean isReady(){
		return ready;
	}
	
	/** print series of choices */
	public void printChoice(Choice[] choiceList){
		for(int i = 0; i < choiceList.length; i++){
			System.out.println((i+1) + ". "+ choiceList[i]);
		}
	}
	
	
}
